package com.ruoyi.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.ClientUser;
import com.ruoyi.common.core.domain.model.LoginBody;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.AppType;
import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.common.httpSdk.client.CommonClient;
import com.ruoyi.common.httpSdk.request.RefreshCookieRequest;
import com.ruoyi.common.httpSdk.sms.RefreshElvesResponse;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.RedisCacheKeyHelper;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.core.domain.BatchClientUserSummary;
import com.ruoyi.core.domain.ClientProduct;
import com.ruoyi.core.domain.ClientProductUser;
import com.ruoyi.core.domain.ClientUserProductRelation;
import com.ruoyi.core.domain.custombean.ClientUserProductRelationMoreInfo;
import com.ruoyi.core.manager.ProductUserCookieResourceManager;
import com.ruoyi.core.mapper.BatchClientUserSummaryMapper;
import com.ruoyi.core.mapper.ClientProductMapper;
import com.ruoyi.core.mapper.ClientProductUserMapper;
import com.ruoyi.core.mapper.ClientUserProductRelationMapper;
import com.ruoyi.core.service.ClientOperationService;
import com.ruoyi.core.service.IClientProductUserService;
import com.ruoyi.core.task.JsPluginRefreshJob;
import com.ruoyi.core.task.JungleScoutCookieRefreshJob;
import com.ruoyi.core.task.MerchantWordCookieRefreshOnOffUpdateJob;
import com.ruoyi.core.vo.*;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.framework.utils.UserLocalThreadHelper;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.mapper.ClientUserMapper;
import com.ruoyi.system.service.ISysConfigService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Description: 客户端用户 操作服务接口
 * @Company: 北京卡拉卡尔股份技术有限公司
 * @Author: Darren Ho
 * @Date: 10/10/2020 16:59
 */
@Service
public class ClientOperationServiceImpl implements ClientOperationService {

    protected final Logger logger = LoggerFactory.getLogger(ClientOperationServiceImpl.class);

    @Autowired
    private TokenService tokenService;

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private ClientProductUserMapper clientProductUserMapper;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private ISysConfigService sysConfigService;

    @Autowired
    private ProductUserCookieResourceManager productUserCookieResourceManager;

    @Autowired
    private ClientUserProductRelationMapper clientUserProductRelationMapper;

    @Autowired
    private IClientProductUserService clientProductUserService;

    @Autowired
    private ClientProductMapper clientProductMapper;

    @Override
    public String login(LoginBody loginBody) {
        String username = loginBody.getUsername();
        String password = loginBody.getPassword();

        // 用户验证
        Authentication authentication;
        try {
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
        } catch (Exception e) {
            if (e instanceof BadCredentialsException) {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                throw new UserPasswordNotMatchException();
            } else {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
                throw new CustomException(e.getMessage());
            }
        }
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        // 更新客户端用户使用状态
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        UserLocalThreadHelper.setLoginUser(loginUser);
        AsyncManager.me().execute(new TimerTask() {
            @Override
            public void run() {
                // 更新用户激活状态
                activeClientUserWhenUserStatusIsNull(loginUser);
                //保存客户端信息

            }
        });

        // 生成token
        String token = tokenService.createToken(loginUser);
        cacheUserProductUseExpireTime(token,loginUser.getUser().getUserId());
        return token;
    }

    private void cacheUserProductUseExpireTime(String token,Long userId) {
        //获取用户的产品使用权限和过期时间
        List<ClientUserProductRelationMoreInfo> relations = clientUserProductRelationMapper.findAllProductPermissionByUserId(userId);
        if(relations == null || relations.isEmpty()){
            return;
        }
        HashMap<String,ClientUserProductRelationMoreInfo> relationMoreInfoHashMap = new HashMap<>();
        for (ClientUserProductRelationMoreInfo relation : relations) {
            relationMoreInfoHashMap.put(relation.getProductName(),relation);
        }
        //缓存至redis
        redisCache.setCacheObject(RedisCacheKeyHelper.getClientProductUseTimeKey(token),relationMoreInfoHashMap);
    }

    @Override
    public Boolean productUserLoginCallback(ProductUserLoginCallback productUserLoginCallback) {

        Assert.isTrue((productUserLoginCallback.getCookies() != null && !productUserLoginCallback.getCookies().isEmpty()), "cookie为空");

        Long productUserId = productUserLoginCallback.getProductUserId();

        ClientProductUser productUser = clientProductUserMapper.selectClientProductUserById(productUserId);
        if (productUser == null) {
            String message = String.format("产品用户 id:[%s]不存在", productUserId);
            throw new CustomException(message);
        }
        //        StringBuilder cookie = new StringBuilder();

        String cookie = productUserLoginCallback.getCookies();
        //        for (int i = 0; i < size; i++) {
        //            Map<String, Object> item = productUserLoginCallback.getCookies().get(i);
        //            cookie.append(item.get("name")).append("=").append(item.get("value"));
        //            if (i < size - 1) {
        //                cookie.append(";");
        //            }
        //
        //        }
        //  更新cookie
        ClientProductUser clientProductUser = new ClientProductUser();
        clientProductUser.setId(productUserId);
        clientProductUser.setCookie(cookie);
        clientProductUser.setUpdateTime(DateUtils.getNowDate());
        clientProductUserMapper.update(clientProductUser);

        // 更新缓存cookie
        productUserCookieResourceManager.setCookie(productUserId, clientProductUser.getCookie());

        //0 0/30 * * * ?
        String productUserLoginTimeConstraint = sysConfigService.selectConfigByKey("PRODUCT_USER_LOGIN_TIME_CONSTRAINT");
        Assert.hasText(productUserLoginTimeConstraint, "product user login time constraint ");

        int productUserLoginTime = Integer.parseInt(productUserLoginTimeConstraint);

        String productUserLoginTimeKey = RedisCacheKeyHelper.getProductUserLoginTimeKey(productUserId, SecurityUtils.getLoginUser().getUser().getUserId());
        logger.info("内置账号登录时间约束--->{}", productUserLoginTime);

        redisCache.setCacheObject(productUserLoginTimeKey, true, productUserLoginTime, TimeUnit.MINUTES);
        return true;
    }

    @Override
    public Boolean productUserLogoutCallback(ProductUserLogoutCallback productUserLogoutCallback) {

        Long productId = productUserLogoutCallback.getProductId();
        Long productUserId = productUserLogoutCallback.getProductUserId();
        Long clientUserId = productUserLogoutCallback.getClientUserId();
        assertProduct2UserMappingEntryAreExit(productUserId, productId);

        // 维护内置账号在线状态
        String key = RedisCacheKeyHelper.getProductUserOnlineUsageKey(productId, productUserId, clientUserId);
        redisCache.deleteObject(key);

        String productUserLoginTimeKey = RedisCacheKeyHelper.getProductUserLoginTimeKey(productUserId, clientUserId);
        redisCache.deleteObject(productUserLoginTimeKey);

        return true;
    }

    @Override
    public Boolean jungleScoutCookieRefresh(Long productId, Long[] productUserIds) {
        JungleScoutCookieRefreshJob bean = SpringUtils.getBean(JungleScoutCookieRefreshJob.class);

        List<ClientProductUser> clientProductUsers;
        if (productUserIds != null && productUserIds.length != 0) {
            clientProductUsers = clientProductUserMapper.findByIdSet(productUserIds);
        } else {
            clientProductUsers = clientProductUserMapper.findByProductId(productId);
        }
        AsyncManager.me().executeRightAway(new TimerTask() {
            @Override
            public void run() {
                bean.start(clientProductUsers);
            }
        });

        return true;
    }

    @Override
    public Boolean jsPluginCookieRefresh(Long productId, Long[] ids) {
        JsPluginRefreshJob bean = SpringUtils.getBean(JsPluginRefreshJob.class);

        List<ClientProductUser> clientProductUsers;
        if (ids != null && ids.length != 0) {
            clientProductUsers = clientProductUserMapper.findByIdSet(ids);
        } else {
            clientProductUsers = clientProductUserMapper.findByProductId(productId);
        }
        AsyncManager.me().executeRightAway(new TimerTask() {
            @Override
            public void run() {
                bean.start(clientProductUsers);
            }
        });

        return true;
    }

    @Override
    public JsPluginLoginDTO loginByPluginJs(LoginBody loginBody) {

        JsPluginLoginDTO dto = new JsPluginLoginDTO();
        //登录
        String token = login(loginBody);
        //获取js插件cookie
        ClientProductUser clientProductUser = getJsPluginProductUser();
        if(clientProductUser == null){
            throw new CustomException("登录失败,无可用账号");
        }
        dto.setDailyToken(clientProductUser.getCookie());
        dto.setUserName(UserLocalThreadHelper.get().getLoginUser().getUsername());
        dto.setToken(token);
        return dto;
    }

    public ClientProductUser getJsPluginProductUser(){
        //获取js插件productId
        ClientProduct clientProduct = clientProductMapper.selectByProductName("js插件");
        if(clientProduct == null){
            throw new CustomException("异常产品");
        }
        Long userId = null;
        try {
            userId = SecurityUtils.getLoginUser().getUser().getUserId();
        } catch (Exception e) {
            userId = UserLocalThreadHelper.get().getLoginUser().getUser().getUserId();
        }
        //获取js插件cookie
        return clientProductUserService.getClientProductUserRandomStrategyByUserId(clientProduct.getId(), userId ,true);
    }

    @Override
    public Boolean cookieRefresh(Long id,AppType appType) {
        if(id == null){
            return false;
        }
        ClientProductUser clientProductUser = clientProductUserMapper.selectClientProductUserById(id);
        if(clientProductUser == null){
            logger.error("刷新cookie失败.不存在的clientProductId:{}",id);
            return false;
        }
        RefreshCookieRequest request = new RefreshCookieRequest(RequestMethod.GET);
        request.setPassword(clientProductUser.getPassword());
        request.setUsername(clientProductUser.getUsername());
        request.setType(appType.getType());

        try {
            RefreshElvesResponse response = new CommonClient().execute(request);
            if(response.getData().getCode() != 200 || StringUtils.isEmpty(response.getData().getCookie())){
                logger.error("刷新{}失败,返回结果:{}",appType.name(),JSON.toJSONString(response.getData()));
                return false;
            }
            logger.info("刷新{}成功 返回结果:{}",appType.name(), JSON.toJSONString(response.getData()));
            //更新cookie
            ClientProductUser productUser = new ClientProductUser();
            productUser.setId(clientProductUser.getId());
            productUser.setCookie(response.getData().getCookie());
            productUser.setUpdateTime(DateUtils.getNowDate());
            productUser.setAvailable(1);
            clientProductUserMapper.update(productUser);
        } catch (Exception e) {
            logger.error("刷新{} cookie失败 e:{}",appType.name(),e.getMessage());
            return false;
        }
        return true;
    }

    @Override
    public Boolean merchantWordCookieRefreshOnOff(Long productId, Long[] productUserIds) {
        MerchantWordCookieRefreshOnOffUpdateJob bean = SpringUtils.getBean(MerchantWordCookieRefreshOnOffUpdateJob.class);

        List<ClientProductUser> clientProductUsers;
        if (productUserIds != null && productUserIds.length != 0) {
            clientProductUsers = clientProductUserMapper.findByIdSet(productUserIds);
        } else {
            clientProductUsers = clientProductUserMapper.findByProductId(productId);
        }

        AsyncManager.me().executeRightAway(new TimerTask() {
            @Override
            public void run() {
                bean.startRightNow(clientProductUsers);
            }
        });
        return true;
    }

    private static int getProductUserLoginTime(String cookieRefreshCorn) {
        String temp = org.apache.commons.lang3.StringUtils.substringAfter(cookieRefreshCorn, "/");

        String resultStr = StringUtils.trimArrayElements(StringUtils.delimitedListToStringArray(temp, "*"))[0];

        return Integer.parseInt(resultStr);
    }

    public static void main(String[] args) {

        System.out.println(getProductUserLoginTime("0 0/12 * * * ? "));

    }

    @Override
    public HeartbeatMessageResult handleHeartbeatMessage(HeartbeatMessage heartbeatMessage) {
        HeartbeatMessageResult result = new HeartbeatMessageResult();

        HeartbeatMessagePayload payload = heartbeatMessage.getPayload();
        Long clientUserId = payload.getClientUserId();
        Long productUserId = payload.getProductUserId();
        Long productId = payload.getProductId();
        BeanUtils.copyProperties(heartbeatMessage.getPayload(), result);

        assertProduct2UserMappingEntryAreExit(productUserId, productId);


        // jungleScoutProduct  处理
        String jungleScoutProductId = sysConfigService.selectConfigByKey("JUNGLE_SCOUT_PRODUCT_ID");

        if (StringUtils.hasText(jungleScoutProductId) && Long.parseLong(jungleScoutProductId) == productId) {
            String productUserOnlineUsageKey = RedisCacheKeyHelper.getProductUserOnlineUsageKey(productId, productUserId, clientUserId);
            redisCache.setCacheObject(productUserOnlineUsageKey, true, 10, TimeUnit.SECONDS);
            result.setCookie(productUserCookieResourceManager.getCookie(productUserId));
            return result;
        }

        //  获取 刷新cookie的开关  亚马孙网站
        CookieRefresh cookieRefresh = CookieRefresh.OFF;
        String merchantWordCookieRefreshOnOffKey = RedisCacheKeyHelper.getMerchantWordCookieRefreshOnOffKey(productUserId);
        long cookieRefreshExpireTime = redisCache.getExpire(merchantWordCookieRefreshOnOffKey, TimeUnit.MILLISECONDS);
        if (cookieRefreshExpireTime != 0) {
            cookieRefresh = CookieRefresh.ON;
        }

        logger.debug("心跳[product user id:{},product id {}]", productUserId, productId);

        // 1、内置账号在线状态维护  刷新10秒
        String productUserOnlineUsageKey = RedisCacheKeyHelper.getProductUserOnlineUsageKey(productId, productUserId, clientUserId);
        redisCache.setCacheObject(productUserOnlineUsageKey, true, 10, TimeUnit.SECONDS);

        //        refreshProductUserExpireTime(clientUserId, );

        //  2 、cookie 刷新开关设置
        String productUserLoginTimeKey = RedisCacheKeyHelper.getProductUserLoginTimeKey(productUserId, clientUserId);
        if (CookieRefresh.ON == cookieRefresh && redisCache.getExpire(productUserLoginTimeKey, TimeUnit.MILLISECONDS) == 0) {

            String productUser2ProductConcurrentLockKey = RedisCacheKeyHelper.getProductUser2ProductConcurrentLockKey(productUserId, productId);
            boolean locked = redisCache
                    .lock(productUser2ProductConcurrentLockKey, productUser2ProductConcurrentLockKey, cookieRefreshExpireTime, TimeUnit.MILLISECONDS);

            if (locked) {
                result.setCookieRefresh(CookieRefresh.ON);
                logger.info("打开开关操作:客户端用户[{}],亚马孙网内置账号[{}]开关打开状态", clientUserId, productUserId);
            }
        }

        //3、填充cookie
        if (CookieRefresh.OFF == result.getCookieRefresh()) {
            result.setCookie(productUserCookieResourceManager.getCookie(productUserId));
        }
        return result;
    }

    private void assertProduct2UserMappingEntryAreExit(Long productUserId, Long productId) {

        if (clientProductUserMapper.findByUserIdAndProductId(productUserId, productId) == null) {
            String message = String.format("产品id[%s]对应内置账号id[%s]的数据不存在！", productId, productUserId);
            throw new CustomException(message);
        }
    }

    //    private void refreshProductUserExpireTime(Long clientUserId, ) {
    //        String productUserOnlineUsageKey = RedisCacheKeyHelper.getProductUserOnlineUsageKey(entry.getProductId(), entry.getProductUserId(), clientUserId);
    //        long expire = redisCache.getExpire(productUserOnlineUsageKey, TimeUnit.MILLISECONDS);
    //        boolean refresh = false;
    //
    //        if (expire == 0) {// 过期或者未设置
    //            refresh = true;
    //        }
    //
    //        if (expire != 0 && expire < 60 * 1000) {//  小于1分钟时 刷新
    //            refresh = true;
    //        }
    //
    //        if (refresh) {
    //            //当前客户端用户登录剩余过期时间
    //            String userKey = RedisCacheKeyHelper.getUserUniqueKey(OperatorUserSystemHolder.get(), clientUserId);
    //            long clientUserExpireTime = redisCache.getExpire(userKey, TimeUnit.MILLISECONDS);
    //
    //            //2)  内置账户在线使用人数 ,刷新延长在线时间
    //            redisCache.setCacheObject(productUserOnlineUsageKey, true, (int) clientUserExpireTime, TimeUnit.MILLISECONDS);
    //
    //        }
    //
    //    }

    private void activeClientUserWhenUserStatusIsNull(LoginUser loginUser) {
        ClientUser clientUser = (ClientUser) loginUser.getUser();
        if (StringUtils.hasText(clientUser.getStatus())) {// 表示激活过
            return;
        }

        clientUser.setStatus(UserStatus.OK.getCode());
        Date nowDate = DateUtils.getNowDate();
        clientUser.setActiveTime(nowDate);
        SpringUtils.getBean(ClientUserMapper.class).update(clientUser);

        //  更新该用户所有产品权限的到期时间
        BatchClientUserSummary batchClientUserSummary = SpringUtils.getBean(BatchClientUserSummaryMapper.class).findByUserId(clientUser.getUserId());
        Date expireTime = DateUtils
                .incrementBaseOnBaseDate(nowDate, batchClientUserSummary.getSegment().intValue(), batchClientUserSummary.getTimeUnit().getNumber());

        ClientUserProductRelationMapper ClientUserProductRelationMapper = SpringUtils.getBean(ClientUserProductRelationMapper.class);
        List<ClientUserProductRelation> clientUserProductRelations = ClientUserProductRelationMapper.findByUserId(clientUser.getUserId());

        if (clientUserProductRelations != null && !clientUserProductRelations.isEmpty()) {
            clientUserProductRelations.forEach(item -> {
                item.setExpireTime(expireTime);
                ClientUserProductRelationMapper.update(item);
            });
        }
    }
}

